package presentation.view_helpers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.el.ELContext;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ValueChangeEvent;

import org.apache.myfaces.shared_impl.util.MessageUtils;

import persistence.CostUnit;
import persistence.LibraryLocation;
import persistence.Periodical;
import persistence.PredefinedEntry;
import persistence.Purchaser;
import persistence.Subscription;
import persistence.Supplier;
import persistence.User;
import presentation.util.BeanUtil;
import business.BusinessObjectAssembler;
import business.PeriodicalLogic;
import business.SubscriptionLogic;
import business.UserManager;
import business.exception.BusinessException;


public class SubscriptionBean {
	private static final String BUNDLE_BASE_NAME = "resources/message";
	private static final String START_STRING_PARAM = "starts_with";
	private static final String SUBSCRIPTION_ID_PARAM = "subscription_id";
	private static final String REVISIT_NEW_SUBSCRIPTION_PARAM = "revisit";
	private Subscription currentSubscription;
	private List<Subscription> subscriptions;
	private List<Periodical> allPeriodicals;
	private SubscriptionLogic subscriptionLogic;
	private PeriodicalLogic periodicalLogic;
	private UserManager userManager;
	private int currentSubscriptionID;
	private int previousSubscriptionID;
	private int nextSubscriptionID;
	private List<Character> startLetters;
	private String titleStartingWith = "a";
	
	public SubscriptionBean() {
		subscriptionLogic = BusinessObjectAssembler.getSubscriptionLogic();
		periodicalLogic = BusinessObjectAssembler.getPeriodicalLogic();
		userManager = BusinessObjectAssembler.getUserManager();
	}
	
	public SubscriptionLogic getSubscriptionLogic() {
		return subscriptionLogic;
	}

	public void setSubscriptionLogic(SubscriptionLogic subscriptionLogic) {
		this.subscriptionLogic = subscriptionLogic;
	}

	public PeriodicalLogic getPeriodicalLogic() {
		return periodicalLogic;
	}

	public void setPeriodicalLogic(PeriodicalLogic periodicalLogic) {
		this.periodicalLogic = periodicalLogic;
	}
	
	private User getCurrentUser() {
		FacesContext fctx = FacesContext.getCurrentInstance();
		ELContext elctx = fctx.getELContext();
		UserBean userBean = (UserBean)elctx.getELResolver().getValue(elctx, null, "userBean");
		User currentUser = userBean.getLoggedOnUser();
		return currentUser;
	}

	public List<Subscription> getAllSubscriptionsWithTitle() {
		try {
				User currentUser = getCurrentUser();
				if (currentUser != null) {
					LibraryLocation location = currentUser.getLocation();
					if(this.userManager.isAdmin(currentUser)) {
						this.subscriptions = this.subscriptionLogic.getAllSubscriptionsWithTitle(titleStartingWith);
					}
					else {
						this.subscriptions = this.subscriptionLogic.getAllSubscriptionsWithTitleForLibraryLocation(titleStartingWith, location);
					}
					/////////////////////FOR TESTING ONLY
					System.out.println("---------------------fetch subscriptions ");
					System.out.println("------------------- number of subs fetchd: "+subscriptions.size());
					for (int i=0; i < subscriptions.size(); i++) {
						System.out.println(i+". Element, "+subscriptions.get(i).getId());
					}
				}
				/////////////////////
		}
		catch (BusinessException be) {
			//TODO: create faces message
			BeanUtil.createGlobalErrorFacesMessage("");
		}
		return this.subscriptions;
	}
	
	public List<Periodical> getAllPeriodicals() {
		List<Periodical> periodicals = null;
		try {
			periodicals = periodicalLogic.getAllPeriodicals();
		}
		catch (BusinessException e) {
			//TODO: create faces message
		}
		return periodicals;
	}
	
	public List<PredefinedEntry> getAllSubscriptionStates() {
		List<PredefinedEntry> states = null;
		try {
			System.out.println("Current Subsription: " + currentSubscription);
			states = subscriptionLogic.getSubscriptionStates();
		}
		catch (BusinessException be) {
			//TODO: create Faces Message
		}
		return states;
	}
	public List<PredefinedEntry> getAllKindsOfAcquisition() {
		List<PredefinedEntry> kindsOfAcquisition = null;
		try {
			kindsOfAcquisition = subscriptionLogic.getAllKindsOfAcquisition();
		}
		catch (BusinessException be) {
			//TODO: create faces message and render immediately
		}
		return kindsOfAcquisition;
	}	
////alternative way, also works for JSF version <= 1.2:
//public List<SelectItem> getAllKindsOfAcquisition() {
//	List<PredefinedEntry> kindsOfAcquisition = null;
//	List<SelectItem> items = new ArrayList<SelectItem>();
//	try {
//		kindsOfAcquisition = subscriptionLogic.getAllKindsOfAcquisition();
//		
//		for (PredefinedEntry kindOfAcquisition : kindsOfAcquisition) {
//			items.add(new SelectItem(kindOfAcquisition, kindOfAcquisition.getEntryValue()));
//		}
//	}
//	catch (BusinessException be) {
//		//TODO: create faces message and render immediately
//	}
//	return items;
//}
	
	public List<CostUnit> getAllCostUnits() {
		List<CostUnit> costUnits = null;
		try {
			if (currentSubscription != null && currentSubscription.getLocation() != null) {	
				costUnits = subscriptionLogic.getCostUnitsForLibraryLocation(currentSubscription.getLocation()); //or this.currentSubscription.getLocation()			
			}
			else {
				costUnits = subscriptionLogic.getCostUnits();
			}
		}
		catch (BusinessException be) {
			BeanUtil.createGlobalErrorFacesMessage("subscription_data_err_allCostUnits");
		}
		return costUnits;
	}
	
	public List<PredefinedEntry> getAllCurrencies() {
		List<PredefinedEntry> currencies = null;
		try {
			currencies = subscriptionLogic.getAllCurrencies();
		}
		catch (BusinessException be) {
			//TODO: create Faces Message
		}
		return currencies;
	}
	
	//TODO: getAllPurchasers
	public List<Purchaser> getAllPurchasers() {
		List<Purchaser> purchasers = null;
		try {
			if (currentSubscription != null && currentSubscription.getLocation() != null)
				purchasers = subscriptionLogic.getAllPurchasersForLibraryLocation(currentSubscription.getLocation());
			else
				purchasers = subscriptionLogic.getAllPurchasers();
		}
		catch (BusinessException be) {
			//TODO: create FacesMessage
		}
		return purchasers;
	}
	
	public List<Supplier> getAllSuppliers() {
		List<Supplier> suppliers = null;
		try {
			suppliers = subscriptionLogic.getAllSuppliers();
		}
		catch(BusinessException be) {
			//TODO: create Faces Message
		}
		return suppliers;
	}
	
	public Subscription getCurrentSubscription() {
		return currentSubscription;
	}
	
	public void setCurrentSubscription(Subscription sub) {
		this.currentSubscription = sub;
	}
	
	public String getTitleStartingWith() {
		return this.titleStartingWith;
	}
	
	private int getPosOfPrevious() {
		System.out.println("calling getPosOfPrevious()");
		int posOfPreviousSubscription=0;
		int posOfCurrentSubscription=getPosOfCurrentSubscription();
		
		if (getPosOfCurrentSubscription()>0) {
			posOfPreviousSubscription=posOfCurrentSubscription-1;
		}
		return posOfPreviousSubscription;
//		return "/subscription-details.xhtml?subscription_id=" + subscriptions.get(posOfPreviousSubscription).getId();
	}
	
	private int getPosOfCurrentSubscription() {
		System.out.println("calling getPosOfCurrentSubscription()");
		int retVal=0;
		int id=0;
		
		Object o = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(SUBSCRIPTION_ID_PARAM);
		if (o != null) {
			id = Integer.valueOf((String)o);
		}
		
		if(subscriptions==null) {
			System.out.println("SUBSCRIPTION LIST IS NULL");
		}
		
		for (int i=0; i < subscriptions.size(); i++) {
			if (subscriptions.get(i).getId()==id) { //repl: currSub.getId() -> id
				retVal = i;
			}
		}
		return retVal;
//		return subscriptions.indexOf(currSub);
	}
	
	public int getPosOfNextSubscription() {
		int posOfNextSubscription=0;
		int posOfCurrentSubscription=getPosOfCurrentSubscription();
		
		if(posOfCurrentSubscription<subscriptions.size()-1) {
			posOfNextSubscription=posOfCurrentSubscription+1;
		} 
		else {
			posOfNextSubscription=posOfCurrentSubscription;
		}
		return posOfNextSubscription;
//		return "/subscription-details.xhtml?subscription_id="+posOfNextSubscription;
	}
	
	public int getNextSubscriptionID() {
		return 
			(this.nextSubscriptionID = this.subscriptions.get(getPosOfNextSubscription()).getId());
	}
	
	public int getCurrentSubscriptionID() {
		return this.currentSubscriptionID;
	}
	public void setCurrentSubscriptionID(int currentSubscriptionID) {
		this.currentSubscriptionID=currentSubscriptionID;
	}
	
	public int getPreviousSubscriptionID() {
		System.out.println("--------------- calling getPreviousSubscriptionID()");
		if (subscriptions == null) {
			//TODO: fill list with subscriptions, based on currentSubscriptionID, 
		}
		//TODO: Message, if last subscription reached
		
		return 
			(this.previousSubscriptionID = subscriptions.get(getPosOfPrevious()).getId());		
//		return this.previousSubscriptionID;
	}
	
//	public void setPreviousSubscriptionID(int prevSubId) {
//		this.previousSubscriptionID = prevSubId;
//	}
	
	/*taken from http://jsfatwork.irian.at/semistatic/advanced_jsf.html#!idx:/advanced_jsf.html:fig:viewparam-event-code */
	public void preRenderDetailsView(ComponentSystemEvent ev) {	
		System.out.println("Entering PreRenderView------------");
		
		int id=0;
		FacesContext ctx = FacesContext.getCurrentInstance();
		FacesMessage errMsg = null;
		//parameter value indicating for which subscription to display the details
		//TODO: replace param String with constant
		Object o = ctx.getExternalContext().getRequestParameterMap().get(SUBSCRIPTION_ID_PARAM);
		if (o != null) {
			id = Integer.valueOf((String)o);
		}
		//TODO: is it OK to set subscription with id 0 as current one?
		setCurrentSubscriptionID(id);

		System.out.println("Id of current Subscription: " + getCurrentSubscriptionID());
		
		if (!ctx.isValidationFailed()) {
			try {
				this.currentSubscription = subscriptionLogic.findGivenSubscription(currentSubscriptionID);
				
				//set it to null, so that it won't be used for later requests without parameter
				currentSubscriptionID=0;
				
				if (this.currentSubscription==null) {
					errMsg = MessageUtils.getMessage(BUNDLE_BASE_NAME, FacesMessage.SEVERITY_ERROR, 
											"subscription_details_err_missing_id", null);				
					ctx.addMessage(null, errMsg);									
				}
				throw new BusinessException("Exception");
			}
			catch (BusinessException be) {
				//TODO: handle exception
				errMsg = MessageUtils.getMessage(BUNDLE_BASE_NAME, FacesMessage.SEVERITY_ERROR,
										"", new Object[]{String.valueOf(currentSubscriptionID)});
			}
		}
	}
	
	public void preRenderNewView(ComponentSystemEvent ev) {
		ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext();
		String revisitParam = ectx.getRequestParameterMap().get(REVISIT_NEW_SUBSCRIPTION_PARAM);
		Boolean revisit = Boolean.FALSE;
		if (revisitParam != null)
		{
			revisit = Boolean.valueOf(revisitParam);
		}
		if (!revisit) {
			this.currentSubscription = subscriptionLogic.getNewEmptySubscription();
		/* 
		 * ensures, that a user for a library location may only select the cost units
		 * and purchasers belonging to his location:
		 */		
			this.currentSubscription.setLocation(getCurrentUser().getLocation());
		}
	}
	
	/**
	 * prepares the data needed for display
	 * 
	 * @param ev
	 */
	public void preRenderOverview(ComponentSystemEvent ev) {

		FacesContext ctx = FacesContext.getCurrentInstance();
		//indicates for which periodical titles to display subscriptions
		Object o = ctx.getExternalContext().getRequestParameterMap().get(START_STRING_PARAM);
		if (o != null) {
			this.titleStartingWith = (String)o;
		}
	}
	
	public List<Character> getStartLetters() {
		try {
//			if (startLetters != null) {
//				if (!startLetters.isEmpty()) {
//					startLetters.clear();
//				}
//			}
			List<Subscription> subscriptions = null;
			User currentUser = getCurrentUser();
			if (currentUser != null) {
				if (userManager.isAdmin(currentUser)) {
					subscriptions = subscriptionLogic.getAllSubscriptions();
				}
				else {
					subscriptions = subscriptionLogic.getAllSubscriptionsForLibraryLocation(currentUser.getLocation());
				}
				List<String> titles = new ArrayList<String>();				
				for (Subscription sub : subscriptions) {
					titles.add(sub.getPeriodical().getTitle());
				}
				startLetters = BeanUtil.getStartLetters(titles);
			}
		}
		catch(BusinessException be) {
			//TODO: create faces message
		}
		System.out.println("-----OUTPUTTING List--------" + startLetters);
		return Collections.unmodifiableList(startLetters);				
	}
	
	public String createNewSubscription() {
		String retVal = "failed";
		User currentUser = getCurrentUser();
		/*second check is needed, if admin is logged on and selects library location
		 * for which to save the subscription already on UI, subscription-data.xhtml (SubscriptionValueChangeListener)
		 */
		
		if (currentUser.getLocation() != null && currentSubscription.getLocation() == null)
			currentSubscription.setLocation(getCurrentUser().getLocation());
		
		try {
			/*currentSubscription =*/ subscriptionLogic.saveNewSubscription(currentSubscription);
			BeanUtil.createGlobalErrorFacesMessage("new_subscription_save_succeeded");
			retVal = "succeeded";
		}
//		catch (BusinessException be) {
//			BeanUtil.createGlobalErrorFacesMessage("new_subscription_save_failed");
//		}
		catch (Throwable t) {
			t.printStackTrace();
			BeanUtil.createGlobalErrorFacesMessage("new_subscription_save_failed");
		}
		
		return retVal;
	}
	
	public String updateSubscription() {

		String retVal = "notUpdated";
		try {
			subscriptionLogic.updateSubscription(this.currentSubscription);
			retVal = "updated";
			BeanUtil.createGlobalErrorFacesMessage("subscription_details_update_successful");
		}
		catch (Throwable t) {	
			t.printStackTrace();
			BeanUtil.createGlobalErrorFacesMessage("subscription_details_err_not_updated");
		}
		System.out.println(">>>>>>>>>>>>>> UPDATED: " + retVal);
		return retVal;
	}
	
	public void costUnitChanged(ValueChangeEvent vce) {
		System.out.println("VALUE FOR costUnit changed: Old value=" + vce.getOldValue() + ", New Value=" + vce.getNewValue());
	}	
}